package com.stewiechen.aipet;

import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.os.*;
import android.text.TextUtils;
import android.util.Log;
import android.view.*;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.ImageView;

import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import androidx.recyclerview.widget.SimpleItemAnimator;
import com.google.gson.Gson;

import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import org.jetbrains.annotations.NotNull;
import pl.droidsonroids.gif.GifDrawable;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;

public class FloatingWindowService extends Service {
    private static final String TAG = "FloatingWindowService";

    private static final int ROUND = 50;

    private static final int STEP = 5;

    private static final int RECONNECT = 3000;

    private boolean isExit = false;

    private WindowManager windowManager;

    private Handler mainHandler = new Handler(Looper.getMainLooper()) {
        int count = 0;

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case Constant.Event.STAY: {
                    stay();
                    break;
                }
                case Constant.Event.RUN_BOTTOM_LEFT: {
                    params.x = params.x - STEP;
                    params.y = params.y + STEP;
                    // TODO: 限制边界
                    boundaryConstraint();
                    windowManager.updateViewLayout(view, params);
                    if (count < 30) {
                        mainHandler.sendEmptyMessageDelayed(Constant.Event.RUN_BOTTOM_LEFT, ROUND);
                        count++;
                    } else {
                        mainHandler.sendEmptyMessage(Constant.Event.EVENT_LOOP);
                        count = 0;
                    }
                    break;
                }
                case Constant.Event.RUN_BOTTOM_RIGHT: {
                    params.x = params.x + STEP;
                    params.y = params.y + STEP;
                    boundaryConstraint();
                    windowManager.updateViewLayout(view, params);
                    if (count < 30) {
                        mainHandler.sendEmptyMessageDelayed(Constant.Event.RUN_BOTTOM_RIGHT, ROUND);
                        count++;
                    } else {
                        mainHandler.sendEmptyMessage(Constant.Event.EVENT_LOOP);
                        count = 0;
                    }
                    break;
                }
                case Constant.Event.RUN_TOP_LEFT: {
                    params.x = params.x - STEP;
                    params.y = params.y - STEP;
                    boundaryConstraint();
                    windowManager.updateViewLayout(view, params);
                    if (count < 30) {
                        mainHandler.sendEmptyMessageDelayed(Constant.Event.RUN_TOP_LEFT, ROUND);
                        count++;
                    } else {
                        mainHandler.sendEmptyMessage(Constant.Event.EVENT_LOOP);
                        count = 0;
                    }
                    break;
                }
                case Constant.Event.RUN_TOP_RIGHT: {
                    params.x = params.x + STEP;
                    params.y = params.y - STEP;
                    boundaryConstraint();
                    windowManager.updateViewLayout(view, params);
                    if (count < 30) {
                        mainHandler.sendEmptyMessageDelayed(Constant.Event.RUN_TOP_RIGHT, ROUND);
                        count++;
                    } else {
                        mainHandler.sendEmptyMessage(Constant.Event.EVENT_LOOP);
                        count = 0;
                    }
                    break;
                }
                case Constant.Event.EVENT_LOOP: {
                    int eventType = generateRandomNumber(3);
                    switch (eventType) {
                        case 0: {
                            int direction = generateRandomNumber(4);
                            switch (direction) {
                                case 0:
                                    runBottomLeft();
                                    break;
                                case 1:
                                    runBottomRight();
                                    break;
                                case 2:
                                    runTopLeft();
                                    break;
                                case 3:
                                    runTopRight();
                                    break;
                                default:
                                    mainHandler.sendEmptyMessage(Constant.Event.STAY);
                                    break;
                            }
                            break;
                        }
                        default:
                            mainHandler.sendEmptyMessage(Constant.Event.STAY);
                            break;
                    }
                    break;
                }
                case Constant.Event.STOP_ALL: {
                    count = 0;
                    mainHandler.removeMessages(Constant.Event.STAY);
                    mainHandler.removeMessages(Constant.Event.RUN_BOTTOM_LEFT);
                    mainHandler.removeMessages(Constant.Event.RUN_BOTTOM_RIGHT);
                    mainHandler.removeMessages(Constant.Event.RUN_TOP_LEFT);
                    mainHandler.removeMessages(Constant.Event.RUN_TOP_RIGHT);
                    mainHandler.removeMessages(Constant.Event.EVENT_LOOP);
                    break;
                }
            }
        }
    };

    private View view;

    private GestureDetector gestureDetector;

    private WindowManager.LayoutParams params;

    // TODO: 加入screenWidth综合判断图像大小
    private int screenWidth;

    private int screenHeight;

    private int petWidth;

    private int petHeight;

    private int boxWidth;

    private int boxHeight;

    private ImageView pet;

    private View option;

    private View exit;

    private View hide;

    private View toChat;

    private View chatWith;

    private View backOption;

    private RecyclerView chatContent;

    private ChatMsgItemAdapter chatMsgItemAdapter;

    private View sendMsg;

    private EditText msgIn;

    private View clearSession;

    private WebSocket sender;

    private volatile boolean loading = false;

    public FloatingWindowService() {
    }

    @SuppressLint({"InflateParams", "ClickableViewAccessibility"})
    @Override
    public void onCreate() {
        super.onCreate();
        // 创建悬浮窗布局
        view = LayoutInflater.from(this).inflate(R.layout.view_floating, null);
        view.setVisibility(View.GONE);
        pet = view.findViewById(R.id.pet);
        option = view.findViewById(R.id.option);
        hide = view.findViewById(R.id.hide);
        exit = view.findViewById(R.id.exit);
        toChat = view.findViewById(R.id.toChat);
        chatWith = view.findViewById(R.id.chatWith);
        chatContent = view.findViewById(R.id.chatContent);
        chatMsgItemAdapter = new ChatMsgItemAdapter(getApplicationContext(), chatContent);
        sendMsg = view.findViewById(R.id.sendMsg);
        clearSession = view.findViewById(R.id.clearSession);
        msgIn = view.findViewById(R.id.msgIn);
        backOption = view.findViewById(R.id.backOption);

        this.connectAi();

        chatContent.setAdapter(chatMsgItemAdapter);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getApplicationContext());
        linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        chatContent.setLayoutManager(linearLayoutManager);
        ((SimpleItemAnimator) chatContent.getItemAnimator()).setSupportsChangeAnimations(false);

        msgIn.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    if (!v.hasFocus()) {
                        params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
                        windowManager.updateViewLayout(view, params);
                        v.requestFocus();
                        mainHandler.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                                imm.showSoftInput(v, InputMethodManager.SHOW_IMPLICIT);
                            }
                        }, 100);
                    }
                }
                return false;
            }
        });
        msgIn.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (!hasFocus) {
                    params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
                    windowManager.updateViewLayout(view, params);
                }
            }
        });

        sendMsg.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String inputMsg = msgIn.getText().toString();
                if (loading || TextUtils.isEmpty(inputMsg)) {
                    return;
                }
                ChatMsg message = new ChatMsg();
                message.setMsgType(Constant.MessageType.REQ_MSG);
                message.setMsg(inputMsg);
                chatMsgItemAdapter.appendData(message);

                msgIn.setText("");

                RequestMessage req = new RequestMessage();
                req.setCommand(Constant.RequestCommand.SEND);
                req.setContent(inputMsg);
                String json = new Gson().toJson(req);
                loading = true;
                sender.send(json);
            }
        });

        clearSession.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (loading) return;
                RequestMessage req = new RequestMessage();
                req.setCommand(Constant.RequestCommand.CLEAR);
                String json = new Gson().toJson(req);
                loading = true;
                sender.send(json);
            }
        });

        // 设置悬浮窗的参数
        params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT);

        // 初始化窗口管理器
        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);

        Point size = new Point();
        windowManager.getDefaultDisplay().getSize(size);
        screenWidth = size.x;
        screenHeight = size.y;

        // 添加悬浮窗到窗口管理器
        windowManager.addView(view, params);

        gestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onDoubleTap(@NonNull MotionEvent e) {
                if (pet.getVisibility() != View.VISIBLE) {
                    return super.onDoubleTap(e);
                }
                if (option.getVisibility() != View.VISIBLE) {
                    option.setVisibility(View.VISIBLE);
                    int width = option.getLayoutParams().width;
                    params.width += width;
                    windowManager.updateViewLayout(view, params);
                }
                return super.onDoubleTap(e);
            }
        });

        // 设置悬浮窗的拖动监听器
        view.setOnTouchListener(new View.OnTouchListener() {
            private int initialX;
            private int initialY;
            private float initialTouchX;
            private float initialTouchY;

            @SuppressLint("ClickableViewAccessibility")
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                gestureDetector.onTouchEvent(event);
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        // 记录初始位置和触摸点
                        initialX = params.x;
                        initialY = params.y;
                        initialTouchX = event.getRawX();
                        initialTouchY = event.getRawY();
                        return false;
                    case MotionEvent.ACTION_MOVE:
                        // 计算偏移量并更新悬浮窗位置
                        params.x = initialX + (int) (event.getRawX() - initialTouchX);
                        params.y = initialY + (int) (event.getRawY() - initialTouchY);
                        windowManager.updateViewLayout(view, params);
                        return false;
                }
                return false;
            }
        });

        // 设置在View消失时Service可以销毁
        view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
            @Override
            public void onViewAttachedToWindow(@NonNull View v) {

            }

            @Override
            public void onViewDetachedFromWindow(@NonNull View v) {
                stopSelf();
            }
        });

        exit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                isExit = true;
                sender.cancel();
                view.setVisibility(View.GONE);
                mainHandler.removeCallbacksAndMessages(null);
                stopSelf();
            }
        });

        hide.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int width = option.getLayoutParams().width;
                params.width -= width;
                option.setVisibility(View.GONE);
                windowManager.updateViewLayout(view, params);
            }
        });

        toChat.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                pet.setVisibility(View.GONE);
                params.width = chatWith.getLayoutParams().width;
                params.height = chatWith.getLayoutParams().height;
                mainHandler.sendEmptyMessage(Constant.Event.STOP_ALL);
                option.setVisibility(View.GONE);
                chatWith.setVisibility(View.VISIBLE);
                windowManager.updateViewLayout(view, params);
            }
        });

        backOption.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                chatWith.setVisibility(View.GONE);
                pet.setVisibility(View.VISIBLE);
                show();
            }
        });

        show();
    }

    private GifDrawable init() {
        GifDrawable anim;
        try {
            anim = new GifDrawable(getAssets(), Constant.Animation.STAY[0]);
        } catch (IOException e) {
            Log.e(TAG, e.getMessage(), e);
            return null;
        }
        drawAnimation(anim);
        int ms = anim.getDuration();
        mainHandler.sendEmptyMessageDelayed(Constant.Event.EVENT_LOOP, ms + ROUND);
        return anim;
    }

    private void connectAi() {
        sender = Api.newWebSocket(Constant.Apis.AI_MSG_PUSH, new WebSocketListener() {
            private volatile boolean firstMsg = true;

            @Override
            public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, @Nullable okhttp3.Response response) {
                super.onFailure(webSocket, t, response);
                Log.e(TAG, t.getMessage(), t);
                loading = false;
                if (!isExit) {
                    reconnectAi();
                }
            }

            @Override
            public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String reason) {
                super.onClosed(webSocket, code, reason);
                loading = false;
                if (!isExit) {
                    reconnectAi();
                }
            }

            @Override
            public void onOpen(@NonNull WebSocket webSocket, @NonNull okhttp3.Response response) {
                super.onOpen(webSocket, response);
                loading = false;
            }

            @Override
            public void onMessage(@NonNull WebSocket webSocket, @NonNull String text) {
                super.onMessage(webSocket, text);
                Response response = new Gson().fromJson(text, Response.class);
                if (!response.getCode().equals(Constant.ApiStatusCode.OK)) {
                    return;
                }
                switch (response.getData().getCallbackStatus().intValue()) {
                    case Constant.CallbackStatus.MESSAGE: {
                        String content = response.getData().getContent();
                        if (firstMsg) {
                            firstMsg = false;
                            ChatMsg chatMsg = new ChatMsg();
                            chatMsg.setMsg(content);
                            chatMsg.setMsgType(Constant.MessageType.AI_MSG);
                            chatMsgItemAdapter.appendData(chatMsg);
                        } else {
                            chatMsgItemAdapter.updateLastMsg(content);
                        }
                        break;
                    }
                    case Constant.CallbackStatus.COMPLETE: {
                        String content = response.getData().getContent();
                        if (!TextUtils.isEmpty(content)) {
                            chatMsgItemAdapter.updateLastMsg(content);
                        }
                        firstMsg = true;
                        loading = false;
                        break;
                    }
                    case Constant.CallbackStatus.NEW_SESSION: {
                        firstMsg = true;
                        loading = false;
                        mainHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                chatMsgItemAdapter.flushData(new ArrayList<>());
                            }
                        });
                        IToast.success(getApplicationContext(), getString(R.string.clearSession));
                        break;
                    }
                }
            }
        });
    }

    private void reconnectAi() {
        sender.cancel();
        try {
            Thread.sleep(RECONNECT);
        } catch (Exception e) {
            Log.e(TAG, e.getMessage(), e);
        }
        connectAi();
    }

    private void stay() {
        GifDrawable anim;
        try {
            int random = generateRandomNumber(Constant.Animation.STAY.length);
            anim = new GifDrawable(getAssets(), Constant.Animation.STAY[random]);
        } catch (IOException e) {
            Log.e(TAG, e.getMessage(), e);
            return;
        }
        drawAnimation(anim);
        int ms = anim.getDuration();
        mainHandler.sendEmptyMessageDelayed(Constant.Event.EVENT_LOOP, ms + ROUND);
    }

    private void runBottomLeft() {
        int random = generateRandomNumber(Constant.Animation.RUN_BOTTOM_LEFT.length);
        GifDrawable anim;
        try {
            anim = new GifDrawable(getAssets(), Constant.Animation.RUN_BOTTOM_LEFT[random]);
        } catch (IOException e) {
            Log.e(TAG, e.getMessage(), e);
            return;
        }
        drawAnimation(anim);
        mainHandler.sendEmptyMessage(Constant.Event.RUN_BOTTOM_LEFT);
    }

    private void runTopLeft() {
        int random = generateRandomNumber(Constant.Animation.RUN_TOP_LEFT.length);
        GifDrawable anim;
        try {
            anim = new GifDrawable(getAssets(), Constant.Animation.RUN_TOP_LEFT[random]);
        } catch (IOException e) {
            Log.e(TAG, e.getMessage(), e);
            return;
        }
        drawAnimation(anim);
        mainHandler.sendEmptyMessage(Constant.Event.RUN_TOP_LEFT);
    }

    private void runBottomRight() {
        int random = generateRandomNumber(Constant.Animation.RUN_BOTTOM_RIGHT.length);
        GifDrawable anim;
        try {
            anim = new GifDrawable(getAssets(), Constant.Animation.RUN_BOTTOM_RIGHT[random]);
        } catch (IOException e) {
            Log.e(TAG, e.getMessage(), e);
            return;
        }
        drawAnimation(anim);
        mainHandler.sendEmptyMessage(Constant.Event.RUN_BOTTOM_RIGHT);
    }

    private void runTopRight() {
        int random = generateRandomNumber(Constant.Animation.RUN_TOP_RIGHT.length);
        GifDrawable anim;
        try {
            anim = new GifDrawable(getAssets(), Constant.Animation.RUN_TOP_RIGHT[random]);
        } catch (IOException e) {
            Log.e(TAG, e.getMessage(), e);
            return;
        }
        drawAnimation(anim);
        mainHandler.sendEmptyMessage(Constant.Event.RUN_TOP_RIGHT);
    }

    /**
     * 边界限制
     */
    private void boundaryConstraint() {
        if (params.x < (-(screenWidth / 2 - petWidth / 2))) {
            params.x = -(screenWidth / 2 - petWidth / 2);
        }
        if (params.x > (screenWidth / 2 - petWidth / 2)) {
            params.x = screenWidth / 2 - petWidth / 2;
        }
        if (params.y < (-(screenHeight / 2 - petHeight / 2))) {
            params.y = -(screenHeight / 2 - petHeight / 2);
        }
        if (params.y > (screenHeight / 2 - petHeight / 2)) {
            params.y = screenHeight / 2 - petHeight / 2;
        }
    }

    private void drawAnimation(Drawable anim) {
        pet.setImageDrawable(anim);
    }

    /**
     * range: [0, n)
     */
    private static int generateRandomNumber(int n) {
        return new Random().nextInt(n);
    }

    private void show() {
        GifDrawable initAnim = init();
        if (initAnim == null) return;
        int ratio = initAnim.getIntrinsicWidth() / initAnim.getIntrinsicHeight();
        boxHeight = screenHeight / 4;
        boxWidth = boxHeight * ratio;
        petWidth = boxWidth;
        petHeight = boxHeight;
        // TODO: 目前将盒子设置为正方形 ImageView fitCenter
        params.width = boxWidth;
        params.height = boxHeight;
        params.format = PixelFormat.RGBA_8888;
        params.x = 0;
        params.y = 0;
        windowManager.updateViewLayout(view, params);

        view.setVisibility(View.VISIBLE);
    }

    private void dismiss() {
        view.setVisibility(View.GONE);
        windowManager.removeView(view);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        isExit = true;
        sender.cancel();
        if (windowManager != null) {
            windowManager.removeView(view);
            windowManager = null;
        }
        mainHandler.removeCallbacksAndMessages(null);
        mainHandler = null;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new Binder();
    }
}